This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.

Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Cmd+Shift+Enter.

library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ─────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.1 ──
✔ ggplot2 3.3.5     ✔ purrr   0.3.4
✔ tibble  3.1.0     ✔ dplyr   1.0.9
✔ tidyr   1.2.0     ✔ stringr 1.4.0
✔ readr   2.1.2     ✔ forcats 0.5.1
package ‘tidyr’ was built under R version 4.0.5package ‘readr’ was built under R version 4.0.5── Conflicts ────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
library(survival)
library(survminer)
Loading required package: ggpubr
library(cmprsk)
package ‘cmprsk’ was built under R version 4.0.5
library(tidyverse)
library(caret)
Loading required package: lattice

Attaching package: ‘caret’

The following object is masked from ‘package:survival’:

    cluster

The following object is masked from ‘package:purrr’:

    lift
library(survival)
library(survminer)
library(lubridate)

Attaching package: ‘lubridate’

The following objects are masked from ‘package:base’:

    date, intersect, setdiff, union
ML_edited_features =  read_tsv("/Users/noa/Workspace/raxml_deep_learning_results/ready_raw_data/All_data/ML_edited_features.tsv")
New names:Rows: 439338 Columns: 92── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr  (9): msa_path, msa_path_x, starting_tree_object, starting_tree_type, type, final_tree_topology, msa_path_y, msa_name, feature_msa_type
dbl (82): ...1, Unnamed: 0, Unnamed: 0.1, Unnamed: 0.1.1, Unnamed: 0.1.1.1, Unnamed: 0.1.1.1.1, Unnamed: 0.1.1.1.1.1, Unnamed: 0.1.1.1.1...
lgl  (1): starting_tree_bool
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
raw_final_data = read_tsv("/Users/noa/Workspace/raxml_deep_learning_results/ready_raw_data/All_data/raw_final_performance.tsv")
New names:Rows: 820 Columns: 13── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr  (2): msa_path, starting_tree_type
dbl (11): ...1, starting_tree_ind, spr_radius, spr_cutoff, predicted_failure_probabilities, delta_ll_from_overall_msa_best_topology, tre...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
model_performance = read_tsv("/Users/noa/Workspace/raxml_deep_learning_results/ready_raw_data/All_data/final_performance_comp.tsv")
New names:Rows: 103 Columns: 18── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr  (1): msa_path
dbl (17): ...1, total_time_predicted, total_actual_time, status, diff, pct_global_max, mean_diff, n_distinct_topologies, n_trees_used, U...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
model_performance<- model_performance %>% mutate (time_imprv = curr_sample_total_time/total_actual_time, accuracy_diff =diff-curr_sample_err )

Summary statistics

ML_edited_features %>% distinct (msa_path)
ML_edited_features  %>% distinct (spr_radius)
ML_edited_features  %>% distinct (spr_cutoff)
per_msa_features<- ML_edited_features %>% distinct(msa_path, feature_n_loci, feature_n_seq )

per_msa_features %>%  ggplot(aes(x=feature_n_seq)) + geom_histogram(color="darkblue", fill="purple")+
  labs(title="Number of sequences",x="Number of sequences", y = "Count")+theme(axis.text=element_text(size=15))

per_msa_features %>%  ggplot(aes(x=feature_n_loci)) + geom_histogram(color="darkblue", fill="orange")+
  labs(title="Number of MSA positions",x="Number of MSA positions", y = "Count")+theme(axis.text=element_text(size=15))

Efficiency

model_performance %>% ggplot(aes(x=time_imprv)) + geom_histogram(color="darkblue", fill="lightblue")+
  labs(title="Running-time improvement between default cofnfiguration to ML-based tree search",x="Running-time improvement", y = "Count")+theme(axis.text=element_text(size=14),axis.title=element_text(size=14,face="bold"))

summary(model_performance %>% pull(time_imprv))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.8004  1.6032  5.0377  5.6808  8.4668 26.3827 

Accuracy

model_performance %>% ggplot(aes(x=accuracy_diff)) + geom_histogram(color="darkblue", fill="pink")+
  labs(title="Log-likelihood difference between default cofnfiguration to ML-based tree search",x="Log-likelihood diff", y = "Count")+theme(axis.text=element_text(size=14),axis.title=element_text(size=14,face="bold"))


summary(model_performance %>% pull(diff))
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
0.000000 0.000000 0.000000 0.199719 0.000248 4.829481 
summary(model_performance %>% pull(curr_sample_err))
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
0.000000 0.000000 0.000000 0.087392 0.003121 3.530084 
summary(ML_edited_features %>% pull(delta_ll_from_overall_msa_best_topology))
     Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
  0.00000   0.00000   0.00017   1.47640   1.48819 144.51634 

Overall performance

model_performance<- model_performance %>% mutate (is_better_time = time_imprv>1, is_more_accurate = accuracy_diff<=0) 
model_performance %>% group_by(is_better_time,is_more_accurate) %>% count()

model_performance<- model_performance %>% mutate (is_better_time = time_imprv>1, is_more_accurate = accuracy_diff<=0) 
model_performance %>% group_by(is_better_time,is_more_accurate) %>% summarise(median_accuracy_diff = median(accuracy_diff) , median_running_time_imprv = median(time_imprv))
`summarise()` has grouped output by 'is_better_time'. You can override using the `.groups` argument.

ML-chosen parameters


raw_final_data %>% group_by(msa_path) %>% count() %>% ggplot(aes(x=n)) + geom_histogram(color = "purple", fill = "lightblue")+
  labs(title="",x="Number of starting trees", y = "Count")+theme(axis.text=element_text(size=14),axis.title=element_text(size=14,face="bold"))


raw_final_data %>% group_by(msa_path, starting_tree_type) %>% count() %>% ungroup() %>% group_by(msa_path) %>% mutate(total_size = sum(n)) %>% filter (starting_tree_type=="pars") %>% mutate (pct_parsimony = n/total_size) %>% ggplot(aes(x=pct_parsimony)) + geom_histogram(color = "purple", fill = "lightblue")+
  labs(title="",x="Fraction of parsimnoy trees", y = "Count")+theme(axis.text=element_text(size=14),axis.title=element_text(size=14,face="bold"))


raw_final_data %>% ggplot(aes(x=spr_radius)) + geom_histogram(color="darkgreen", fill="green")+
  labs(title="",x="SPR radius", y = "Count")+theme(axis.text=element_text(size=14),axis.title=element_text(size=14,face="bold"))


raw_final_data %>% ggplot(aes(x=spr_cutoff)) + geom_histogram(color="orange", fill="yellow")+
  labs(title="",x="SPR cutoff", y = "Count")+theme(axis.text=element_text(size=14),axis.title=element_text(size=14,face="bold"))

Correlation between features

Pypythia


ML_edited_features %>% ggplot(aes(x = feature_pypythia_msa_difficulty)) + geom_histogram(fill = "purple")

count_per_starting_tree<-ML_edited_features %>% group_by(msa_path,starting_tree_ind, starting_tree_type,tree_clusters_ind,feature_pypythia_msa_difficulty) %>% count() %>% ungroup() %>% group_by(msa_path,feature_pypythia_msa_difficulty, starting_tree_ind,starting_tree_type) %>% count() 
summary(count_per_starting_tree %>% pull(n))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  1.000   1.000   2.000   4.029   5.000  31.000 

count_per_starting_tree_per_MSA<- count_per_starting_tree %>% group_by(msa_path, starting_tree_type,feature_pypythia_msa_difficulty) %>% summarise(median_per_tree = median(n))
`summarise()` has grouped output by 'msa_path', 'starting_tree_type'. You can override using the `.groups` argument.
var_per_starting_tree_per_MSA<- count_per_starting_tree %>% group_by(msa_path, starting_tree_type,feature_pypythia_msa_difficulty) %>% summarise(var_across_trees = var(n))
`summarise()` has grouped output by 'msa_path', 'starting_tree_type'. You can override using the `.groups` argument.
count_per_starting_tree_per_MSA %>% ggplot(aes(x = median_per_tree, fill = starting_tree_type))+ geom_histogram(position = position_dodge(), bins = 10)

var_per_starting_tree_per_MSA %>% ggplot(aes(x = var_across_trees, fill = starting_tree_type))+ geom_histogram(position = position_dodge(), bins = 10)


count_per_starting_tree_per_MSA %>% ggplot(aes(x = median_per_tree, y = feature_pypythia_msa_difficulty, color = starting_tree_type))+ geom_point()


var_per_starting_tree_per_MSA %>% ggplot(aes(x = var_across_trees, y = feature_pypythia_msa_difficulty, color = starting_tree_type))+ geom_point()

lm1<- lm(median_per_tree~(feature_pypythia_msa_difficulty)*starting_tree_type , data =count_per_starting_tree_per_MSA )
summary(lm1)

Call:
lm(formula = median_per_tree ~ (feature_pypythia_msa_difficulty) * 
    starting_tree_type, data = count_per_starting_tree_per_MSA)

Residuals:
    Min      1Q  Median      3Q     Max 
-7.6335 -1.5321 -0.4105  0.7579 17.9380 

Coefficients:
                                                       Estimate Std. Error t value Pr(>|t|)    
(Intercept)                                            -0.03144    0.36520  -0.086    0.931    
feature_pypythia_msa_difficulty                         6.53084    0.97227   6.717 3.89e-11 ***
starting_tree_typerand                                 -0.07326    0.51647  -0.142    0.887    
feature_pypythia_msa_difficulty:starting_tree_typerand 10.95047    1.37500   7.964 6.93e-15 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.168 on 686 degrees of freedom
Multiple R-squared:  0.461, Adjusted R-squared:  0.4586 
F-statistic: 195.6 on 3 and 686 DF,  p-value: < 2.2e-16
plot(fitted(lm1),resid(lm1))

lm2<- lm(var_across_trees~feature_pypythia_msa_difficulty*starting_tree_type , data =var_per_starting_tree_per_MSA )
summary(lm2)

Call:
lm(formula = var_across_trees ~ feature_pypythia_msa_difficulty * 
    starting_tree_type, data = var_per_starting_tree_per_MSA)

Residuals:
    Min      1Q  Median      3Q     Max 
-4.2079 -1.2399 -0.2905  0.6443 19.5277 

Coefficients:
                                                       Estimate Std. Error t value Pr(>|t|)    
(Intercept)                                             -1.2362     0.2494  -4.957 9.03e-07 ***
feature_pypythia_msa_difficulty                          7.0589     0.6639  10.632  < 2e-16 ***
starting_tree_typerand                                   0.4716     0.3527   1.337  0.18157    
feature_pypythia_msa_difficulty:starting_tree_typerand   3.0914     0.9389   3.292  0.00104 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 2.163 on 686 degrees of freedom
Multiple R-squared:  0.385, Adjusted R-squared:  0.3824 
F-statistic: 143.2 on 3 and 686 DF,  p-value: < 2.2e-16
count_per_final_tree_topology<- ML_edited_features %>% group_by(msa_path,starting_tree_ind, starting_tree_type,tree_clusters_ind) %>% count() %>% ungroup() %>% group_by(msa_path,feature_pypythia_msa_difficulty, starting_tree_ind,starting_tree_type) %>% count() 
count_per_SPR_radius<-ML_edited_features %>% group_by(msa_path,spr_radius, starting_tree_type,tree_clusters_ind) %>% count() %>% ungroup() %>% group_by(msa_path,spr_radius, starting_tree_type) %>% count() 
count_per_SPR_radius %>% ggplot(aes(x = n, fill = starting_tree_type))+ geom_histogram()+facet_grid(rows = vars(spr_radius))



count_per_SPR_cutoff<-ML_edited_features %>% group_by(msa_path,spr_cutoff, starting_tree_type,tree_clusters_ind) %>% count() %>% ungroup() %>% group_by(msa_path,spr_cutoff, starting_tree_type) %>% count() 
count_per_SPR_cutoff %>% ggplot(aes(x = n, fill = starting_tree_type))+ geom_histogram()+facet_grid(rows = vars(spr_cutoff))

summary(count_per_SPR_radius %>% pull(n))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  1.000   1.000   1.000   1.572   2.000   6.000 
example<- ML_edited_features %>% filter (msa_path=='/groups/pupko/noaeker/data/ABC_DR/PANDIT/PF00005/ref_msa.aa.phy')
ML_edited_features %>% distinct (msa_path, starting_tree_ind,starting_tree_ll,tree_clusters_ind,feature_mean_branch_length,feature_mean_internal_branch_length,feature_mean_leaf_branch_length,feature_tree_MAD,feature_mean_rf_distance)
ML_edited_features %>% filter (msa_path=="/groups/pupko/noaeker/data/ABC_DR/PANDIT/PF00005/ref_msa.aa.phy")
tree_features_analysis = read_tsv("/Users/noa/Workspace/raxml_deep_learning_results/ready_raw_data/All_data/tree_comparisons.tsv")
New names:Rows: 407853 Columns: 50── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr  (9): msa_path, starting_tree_object, final_tree_topology, starting_tree_type, feature_msa_type, msa_path_other, starting_tree_obje...
dbl (41): ...1, starting_tree_ind, delta_ll_from_overall_msa_best_topology, final_ll, starting_tree_ll, feature_mean_branch_length, fea...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
tree_features_analysis %>% head()
Error in h(simpleError(msg, call)) : 
  error in evaluating the argument 'x' in selecting a method for function 'head': object 'tree_features_analysis' not found
tree_features_analysis %>% head(5)
tree_features_analysis_edited<- tree_features_analysis %>% mutate (LL_diff = delta_ll_from_overall_msa_best_topology_other-delta_ll_from_overall_msa_best_topology, starting_tree_ll_diff = starting_tree_ll- starting_tree_ll_other) %>% mutate (is_better = LL_diff>0.1)

tree_features_analysis_edited %>% ggplot(aes(x = rf_dist_starting_trees, y= rf_dist_final_trees)) + geom_point()+ facet_grid(rows = vars(starting_tree_type), cols = vars(starting_tree_type_other))

NA
NA
tree_features_analysis_edited %>% filter (starting_tree_type==starting_tree_type_other,) %>%  ggplot(aes(y = (LL_diff), x=(starting_tree_ll_diff))) + geom_point()+ facet_grid(rows = vars(starting_tree_type))


tree_features_analysis_edited %>% filter (starting_tree_type==starting_tree_type_other,) %>%  ggplot(aes(y = (rf_dist_final_trees), x=abs((starting_tree_ll_diff)))) + geom_point()+ facet_grid(rows = vars(starting_tree_type))


data_for_ML<-tree_features_analysis_edited %>% select (-starting_tree_ind,-msa_path_other , -starting_tree_ind_other, -starting_tree_object, -starting_tree_object_other,-delta_ll_from_overall_msa_best_topology_other, -final_ll_other, -final_tree_topology, -final_tree_topology_other,-starting_tree_type,-starting_tree_type_other ,-feature_msa_type,-LL_diff,-rf_dist_final_trees,-delta_ll_from_overall_msa_best_topo  )

msas = tree_features_analysis_edited %>% distinct (msa_path) %>% pull(msa_path)
test_sampled_msas = msas[sample(1:length(msas),20)]
test<-  data_for_ML %>% filter (msa_path %in% test_sampled_msas) %>% select (-msa_path)
train<-  data_for_ML %>% filter (!(msa_path %in% test_sampled_msas)) %>% select (-msa_path)


bin_glm<-  glm(is_better ~ . , data = train, family = "binomial")
caret::varImp(bin_glm)

nullmod <- glm(is_better ~1,data = train , family="binomial")
r2 = 1-logLik(bin_glm)/logLik(nullmod)
print(r2)
summary(bin_glm)

Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Cmd+Option+I.

When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Cmd+Shift+K to preview the HTML file).

The preview shows you a rendered HTML copy of the contents of the editor. Consequently, unlike Knit, Preview does not run any R code chunks. Instead, the output of the chunk when it was last run in the editor is displayed.

summary(train)
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIFdoZW4geW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCB0aGUgcmVzdWx0cyBhcHBlYXIgYmVuZWF0aCB0aGUgY29kZS4gCgpUcnkgZXhlY3V0aW5nIHRoaXMgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ21kK1NoaWZ0K0VudGVyKi4gCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoc3Vydml2YWwpCmxpYnJhcnkoc3Vydm1pbmVyKQpsaWJyYXJ5KGNtcHJzaykKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkoc3Vydml2YWwpCmxpYnJhcnkoc3Vydm1pbmVyKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKYGBgCgpgYGB7cn0KTUxfZWRpdGVkX2ZlYXR1cmVzID0gIHJlYWRfdHN2KCIvVXNlcnMvbm9hL1dvcmtzcGFjZS9yYXhtbF9kZWVwX2xlYXJuaW5nX3Jlc3VsdHMvcmVhZHlfcmF3X2RhdGEvQWxsX2RhdGEvTUxfZWRpdGVkX2ZlYXR1cmVzLnRzdiIpCnJhd19maW5hbF9kYXRhID0gcmVhZF90c3YoIi9Vc2Vycy9ub2EvV29ya3NwYWNlL3JheG1sX2RlZXBfbGVhcm5pbmdfcmVzdWx0cy9yZWFkeV9yYXdfZGF0YS9BbGxfZGF0YS9yYXdfZmluYWxfcGVyZm9ybWFuY2UudHN2IikKbW9kZWxfcGVyZm9ybWFuY2UgPSByZWFkX3RzdigiL1VzZXJzL25vYS9Xb3Jrc3BhY2UvcmF4bWxfZGVlcF9sZWFybmluZ19yZXN1bHRzL3JlYWR5X3Jhd19kYXRhL0FsbF9kYXRhL2ZpbmFsX3BlcmZvcm1hbmNlX2NvbXAudHN2IikKCmBgYAoKYGBge3J9Cm1vZGVsX3BlcmZvcm1hbmNlPC0gbW9kZWxfcGVyZm9ybWFuY2UgJT4lIG11dGF0ZSAodGltZV9pbXBydiA9IGN1cnJfc2FtcGxlX3RvdGFsX3RpbWUvdG90YWxfYWN0dWFsX3RpbWUsIGFjY3VyYWN5X2RpZmYgPWRpZmYtY3Vycl9zYW1wbGVfZXJyICkKYGBgCgpTdW1tYXJ5IHN0YXRpc3RpY3MKCmBgYHtyfQpNTF9lZGl0ZWRfZmVhdHVyZXMgJT4lIGRpc3RpbmN0IChtc2FfcGF0aCkKTUxfZWRpdGVkX2ZlYXR1cmVzICAlPiUgZGlzdGluY3QgKHNwcl9yYWRpdXMpCk1MX2VkaXRlZF9mZWF0dXJlcyAgJT4lIGRpc3RpbmN0IChzcHJfY3V0b2ZmKQpwZXJfbXNhX2ZlYXR1cmVzPC0gTUxfZWRpdGVkX2ZlYXR1cmVzICU+JSBkaXN0aW5jdChtc2FfcGF0aCwgZmVhdHVyZV9uX2xvY2ksIGZlYXR1cmVfbl9zZXEgKQoKcGVyX21zYV9mZWF0dXJlcyAlPiUgIGdncGxvdChhZXMoeD1mZWF0dXJlX25fc2VxKSkgKyBnZW9tX2hpc3RvZ3JhbShjb2xvcj0iZGFya2JsdWUiLCBmaWxsPSJwdXJwbGUiKSsKICBsYWJzKHRpdGxlPSJOdW1iZXIgb2Ygc2VxdWVuY2VzIix4PSJOdW1iZXIgb2Ygc2VxdWVuY2VzIiwgeSA9ICJDb3VudCIpK3RoZW1lKGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xNSkpCnBlcl9tc2FfZmVhdHVyZXMgJT4lICBnZ3Bsb3QoYWVzKHg9ZmVhdHVyZV9uX2xvY2kpKSArIGdlb21faGlzdG9ncmFtKGNvbG9yPSJkYXJrYmx1ZSIsIGZpbGw9Im9yYW5nZSIpKwogIGxhYnModGl0bGU9Ik51bWJlciBvZiBNU0EgcG9zaXRpb25zIix4PSJOdW1iZXIgb2YgTVNBIHBvc2l0aW9ucyIsIHkgPSAiQ291bnQiKSt0aGVtZShheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTUpKQpgYGAKCkVmZmljaWVuY3kKCmBgYHtyfQptb2RlbF9wZXJmb3JtYW5jZSAlPiUgZ2dwbG90KGFlcyh4PXRpbWVfaW1wcnYpKSArIGdlb21faGlzdG9ncmFtKGNvbG9yPSJkYXJrYmx1ZSIsIGZpbGw9ImxpZ2h0Ymx1ZSIpKwogIGxhYnModGl0bGU9IlJ1bm5pbmctdGltZSBpbXByb3ZlbWVudCBiZXR3ZWVuIGRlZmF1bHQgY29mbmZpZ3VyYXRpb24gdG8gTUwtYmFzZWQgdHJlZSBzZWFyY2giLHg9IlJ1bm5pbmctdGltZSBpbXByb3ZlbWVudCIsIHkgPSAiQ291bnQiKSt0aGVtZShheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTQpLGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTQsZmFjZT0iYm9sZCIpKQpzdW1tYXJ5KG1vZGVsX3BlcmZvcm1hbmNlICU+JSBwdWxsKHRpbWVfaW1wcnYpKQpgYGAKQWNjdXJhY3kKCmBgYHtyfQptb2RlbF9wZXJmb3JtYW5jZSAlPiUgZ2dwbG90KGFlcyh4PWFjY3VyYWN5X2RpZmYpKSArIGdlb21faGlzdG9ncmFtKGNvbG9yPSJkYXJrYmx1ZSIsIGZpbGw9InBpbmsiKSsKICBsYWJzKHRpdGxlPSJMb2ctbGlrZWxpaG9vZCBkaWZmZXJlbmNlIGJldHdlZW4gZGVmYXVsdCBjb2ZuZmlndXJhdGlvbiB0byBNTC1iYXNlZCB0cmVlIHNlYXJjaCIseD0iTG9nLWxpa2VsaWhvb2QgZGlmZiIsIHkgPSAiQ291bnQiKSt0aGVtZShheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTQpLGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTQsZmFjZT0iYm9sZCIpKQoKc3VtbWFyeShtb2RlbF9wZXJmb3JtYW5jZSAlPiUgcHVsbChkaWZmKSkKCnN1bW1hcnkobW9kZWxfcGVyZm9ybWFuY2UgJT4lIHB1bGwoY3Vycl9zYW1wbGVfZXJyKSkKCnN1bW1hcnkoTUxfZWRpdGVkX2ZlYXR1cmVzICU+JSBwdWxsKGRlbHRhX2xsX2Zyb21fb3ZlcmFsbF9tc2FfYmVzdF90b3BvbG9neSkpCmBgYApPdmVyYWxsIHBlcmZvcm1hbmNlCgpgYGB7cn0KbW9kZWxfcGVyZm9ybWFuY2U8LSBtb2RlbF9wZXJmb3JtYW5jZSAlPiUgbXV0YXRlIChpc19iZXR0ZXJfdGltZSA9IHRpbWVfaW1wcnY+MSwgaXNfbW9yZV9hY2N1cmF0ZSA9IGFjY3VyYWN5X2RpZmY8PTApIAptb2RlbF9wZXJmb3JtYW5jZSAlPiUgZ3JvdXBfYnkoaXNfYmV0dGVyX3RpbWUsaXNfbW9yZV9hY2N1cmF0ZSkgJT4lIGNvdW50KCkKCm1vZGVsX3BlcmZvcm1hbmNlPC0gbW9kZWxfcGVyZm9ybWFuY2UgJT4lIG11dGF0ZSAoaXNfYmV0dGVyX3RpbWUgPSB0aW1lX2ltcHJ2PjEsIGlzX21vcmVfYWNjdXJhdGUgPSBhY2N1cmFjeV9kaWZmPD0wKSAKbW9kZWxfcGVyZm9ybWFuY2UgJT4lIGdyb3VwX2J5KGlzX2JldHRlcl90aW1lLGlzX21vcmVfYWNjdXJhdGUpICU+JSBzdW1tYXJpc2UobWVkaWFuX2FjY3VyYWN5X2RpZmYgPSBtZWRpYW4oYWNjdXJhY3lfZGlmZikgLCBtZWRpYW5fcnVubmluZ190aW1lX2ltcHJ2ID0gbWVkaWFuKHRpbWVfaW1wcnYpKQpgYGAKCgpNTC1jaG9zZW4gcGFyYW1ldGVycwoKCmBgYHtyfQoKcmF3X2ZpbmFsX2RhdGEgJT4lIGdyb3VwX2J5KG1zYV9wYXRoKSAlPiUgY291bnQoKSAlPiUgZ2dwbG90KGFlcyh4PW4pKSArIGdlb21faGlzdG9ncmFtKGNvbG9yID0gInB1cnBsZSIsIGZpbGwgPSAibGlnaHRibHVlIikrCiAgbGFicyh0aXRsZT0iIix4PSJOdW1iZXIgb2Ygc3RhcnRpbmcgdHJlZXMiLCB5ID0gIkNvdW50IikrdGhlbWUoYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTE0KSxheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTE0LGZhY2U9ImJvbGQiKSkKCnJhd19maW5hbF9kYXRhICU+JSBncm91cF9ieShtc2FfcGF0aCwgc3RhcnRpbmdfdHJlZV90eXBlKSAlPiUgY291bnQoKSAlPiUgdW5ncm91cCgpICU+JSBncm91cF9ieShtc2FfcGF0aCkgJT4lIG11dGF0ZSh0b3RhbF9zaXplID0gc3VtKG4pKSAlPiUgZmlsdGVyIChzdGFydGluZ190cmVlX3R5cGU9PSJwYXJzIikgJT4lIG11dGF0ZSAocGN0X3BhcnNpbW9ueSA9IG4vdG90YWxfc2l6ZSkgJT4lIGdncGxvdChhZXMoeD1wY3RfcGFyc2ltb255KSkgKyBnZW9tX2hpc3RvZ3JhbShjb2xvciA9ICJwdXJwbGUiLCBmaWxsID0gImxpZ2h0Ymx1ZSIpKwogIGxhYnModGl0bGU9IiIseD0iRnJhY3Rpb24gb2YgcGFyc2ltbm95IHRyZWVzIiwgeSA9ICJDb3VudCIpK3RoZW1lKGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xNCksYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xNCxmYWNlPSJib2xkIikpCgpyYXdfZmluYWxfZGF0YSAlPiUgZ2dwbG90KGFlcyh4PXNwcl9yYWRpdXMpKSArIGdlb21faGlzdG9ncmFtKGNvbG9yPSJkYXJrZ3JlZW4iLCBmaWxsPSJncmVlbiIpKwogIGxhYnModGl0bGU9IiIseD0iU1BSIHJhZGl1cyIsIHkgPSAiQ291bnQiKSt0aGVtZShheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTQpLGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTQsZmFjZT0iYm9sZCIpKQoKcmF3X2ZpbmFsX2RhdGEgJT4lIGdncGxvdChhZXMoeD1zcHJfY3V0b2ZmKSkgKyBnZW9tX2hpc3RvZ3JhbShjb2xvcj0ib3JhbmdlIiwgZmlsbD0ieWVsbG93IikrCiAgbGFicyh0aXRsZT0iIix4PSJTUFIgY3V0b2ZmIiwgeSA9ICJDb3VudCIpK3RoZW1lKGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xNCksYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xNCxmYWNlPSJib2xkIikpCmBgYApDb3JyZWxhdGlvbiBiZXR3ZWVuIGZlYXR1cmVzCgoKUHlweXRoaWEKCmBgYHtyfQoKTUxfZWRpdGVkX2ZlYXR1cmVzICU+JSBnZ3Bsb3QoYWVzKHggPSBmZWF0dXJlX3B5cHl0aGlhX21zYV9kaWZmaWN1bHR5KSkgKyBnZW9tX2hpc3RvZ3JhbShmaWxsID0gInB1cnBsZSIpCgpgYGAKCgoKYGBge3J9CmNvdW50X3Blcl9zdGFydGluZ190cmVlPC1NTF9lZGl0ZWRfZmVhdHVyZXMgJT4lIGdyb3VwX2J5KG1zYV9wYXRoLHN0YXJ0aW5nX3RyZWVfaW5kLCBzdGFydGluZ190cmVlX3R5cGUsdHJlZV9jbHVzdGVyc19pbmQsZmVhdHVyZV9weXB5dGhpYV9tc2FfZGlmZmljdWx0eSkgJT4lIGNvdW50KCkgJT4lIHVuZ3JvdXAoKSAlPiUgZ3JvdXBfYnkobXNhX3BhdGgsZmVhdHVyZV9weXB5dGhpYV9tc2FfZGlmZmljdWx0eSwgc3RhcnRpbmdfdHJlZV9pbmQsc3RhcnRpbmdfdHJlZV90eXBlKSAlPiUgY291bnQoKSAKc3VtbWFyeShjb3VudF9wZXJfc3RhcnRpbmdfdHJlZSAlPiUgcHVsbChuKSkKYGBgCgoKYGBge3J9Cgpjb3VudF9wZXJfc3RhcnRpbmdfdHJlZV9wZXJfTVNBPC0gY291bnRfcGVyX3N0YXJ0aW5nX3RyZWUgJT4lIGdyb3VwX2J5KG1zYV9wYXRoLCBzdGFydGluZ190cmVlX3R5cGUsZmVhdHVyZV9weXB5dGhpYV9tc2FfZGlmZmljdWx0eSkgJT4lIHN1bW1hcmlzZShtZWRpYW5fcGVyX3RyZWUgPSBtZWRpYW4obikpCnZhcl9wZXJfc3RhcnRpbmdfdHJlZV9wZXJfTVNBPC0gY291bnRfcGVyX3N0YXJ0aW5nX3RyZWUgJT4lIGdyb3VwX2J5KG1zYV9wYXRoLCBzdGFydGluZ190cmVlX3R5cGUsZmVhdHVyZV9weXB5dGhpYV9tc2FfZGlmZmljdWx0eSkgJT4lIHN1bW1hcmlzZSh2YXJfYWNyb3NzX3RyZWVzID0gdmFyKG4pKQoKCmNvdW50X3Blcl9zdGFydGluZ190cmVlX3Blcl9NU0EgJT4lIGdncGxvdChhZXMoeCA9IG1lZGlhbl9wZXJfdHJlZSwgZmlsbCA9IHN0YXJ0aW5nX3RyZWVfdHlwZSkpKyBnZW9tX2hpc3RvZ3JhbShwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKCksIGJpbnMgPSAxMCkKdmFyX3Blcl9zdGFydGluZ190cmVlX3Blcl9NU0EgJT4lIGdncGxvdChhZXMoeCA9IHZhcl9hY3Jvc3NfdHJlZXMsIGZpbGwgPSBzdGFydGluZ190cmVlX3R5cGUpKSsgZ2VvbV9oaXN0b2dyYW0ocG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgpLCBiaW5zID0gMTApCgpjb3VudF9wZXJfc3RhcnRpbmdfdHJlZV9wZXJfTVNBICU+JSBnZ3Bsb3QoYWVzKHggPSBtZWRpYW5fcGVyX3RyZWUsIHkgPSBmZWF0dXJlX3B5cHl0aGlhX21zYV9kaWZmaWN1bHR5LCBjb2xvciA9IHN0YXJ0aW5nX3RyZWVfdHlwZSkpKyBnZW9tX3BvaW50KCkKCgp2YXJfcGVyX3N0YXJ0aW5nX3RyZWVfcGVyX01TQSAlPiUgZ2dwbG90KGFlcyh4ID0gdmFyX2Fjcm9zc190cmVlcywgeSA9IGZlYXR1cmVfcHlweXRoaWFfbXNhX2RpZmZpY3VsdHksIGNvbG9yID0gc3RhcnRpbmdfdHJlZV90eXBlKSkrIGdlb21fcG9pbnQoKQpgYGAKYGBge3J9CmxtMTwtIGxtKG1lZGlhbl9wZXJfdHJlZX4oZmVhdHVyZV9weXB5dGhpYV9tc2FfZGlmZmljdWx0eSkqc3RhcnRpbmdfdHJlZV90eXBlICwgZGF0YSA9Y291bnRfcGVyX3N0YXJ0aW5nX3RyZWVfcGVyX01TQSApCnN1bW1hcnkobG0xKQpwbG90KGZpdHRlZChsbTEpLHJlc2lkKGxtMSkpCmxtMjwtIGxtKHZhcl9hY3Jvc3NfdHJlZXN+ZmVhdHVyZV9weXB5dGhpYV9tc2FfZGlmZmljdWx0eSpzdGFydGluZ190cmVlX3R5cGUgLCBkYXRhID12YXJfcGVyX3N0YXJ0aW5nX3RyZWVfcGVyX01TQSApCnN1bW1hcnkobG0yKQpgYGAKYGBge3J9CmNvdW50X3Blcl9maW5hbF90cmVlX3RvcG9sb2d5PC0gTUxfZWRpdGVkX2ZlYXR1cmVzICU+JSBncm91cF9ieShtc2FfcGF0aCxzdGFydGluZ190cmVlX2luZCwgc3RhcnRpbmdfdHJlZV90eXBlLHRyZWVfY2x1c3RlcnNfaW5kKSAlPiUgY291bnQoKSAlPiUgdW5ncm91cCgpICU+JSBncm91cF9ieShtc2FfcGF0aCxmZWF0dXJlX3B5cHl0aGlhX21zYV9kaWZmaWN1bHR5LCBzdGFydGluZ190cmVlX2luZCxzdGFydGluZ190cmVlX3R5cGUpICU+JSBjb3VudCgpIApgYGAKCgpgYGB7cn0KY291bnRfcGVyX1NQUl9yYWRpdXM8LU1MX2VkaXRlZF9mZWF0dXJlcyAlPiUgZ3JvdXBfYnkobXNhX3BhdGgsc3ByX3JhZGl1cywgc3RhcnRpbmdfdHJlZV90eXBlLHRyZWVfY2x1c3RlcnNfaW5kKSAlPiUgY291bnQoKSAlPiUgdW5ncm91cCgpICU+JSBncm91cF9ieShtc2FfcGF0aCxzcHJfcmFkaXVzLCBzdGFydGluZ190cmVlX3R5cGUpICU+JSBjb3VudCgpIApjb3VudF9wZXJfU1BSX3JhZGl1cyAlPiUgZ2dwbG90KGFlcyh4ID0gbiwgZmlsbCA9IHN0YXJ0aW5nX3RyZWVfdHlwZSkpKyBnZW9tX2hpc3RvZ3JhbSgpK2ZhY2V0X2dyaWQocm93cyA9IHZhcnMoc3ByX3JhZGl1cykpCgoKY291bnRfcGVyX1NQUl9jdXRvZmY8LU1MX2VkaXRlZF9mZWF0dXJlcyAlPiUgZ3JvdXBfYnkobXNhX3BhdGgsc3ByX2N1dG9mZiwgc3RhcnRpbmdfdHJlZV90eXBlLHRyZWVfY2x1c3RlcnNfaW5kKSAlPiUgY291bnQoKSAlPiUgdW5ncm91cCgpICU+JSBncm91cF9ieShtc2FfcGF0aCxzcHJfY3V0b2ZmLCBzdGFydGluZ190cmVlX3R5cGUpICU+JSBjb3VudCgpIApjb3VudF9wZXJfU1BSX2N1dG9mZiAlPiUgZ2dwbG90KGFlcyh4ID0gbiwgZmlsbCA9IHN0YXJ0aW5nX3RyZWVfdHlwZSkpKyBnZW9tX2hpc3RvZ3JhbSgpK2ZhY2V0X2dyaWQocm93cyA9IHZhcnMoc3ByX2N1dG9mZikpCmBgYApgYGB7cn0Kc3VtbWFyeShjb3VudF9wZXJfU1BSX3JhZGl1cyAlPiUgcHVsbChuKSkKYGBgCgpgYGB7cn0KZXhhbXBsZTwtIE1MX2VkaXRlZF9mZWF0dXJlcyAlPiUgZmlsdGVyIChtc2FfcGF0aD09Jy9ncm91cHMvcHVwa28vbm9hZWtlci9kYXRhL0FCQ19EUi9QQU5ESVQvUEYwMDAwNS9yZWZfbXNhLmFhLnBoeScpCk1MX2VkaXRlZF9mZWF0dXJlcyAlPiUgZGlzdGluY3QgKG1zYV9wYXRoLCBzdGFydGluZ190cmVlX2luZCxzdGFydGluZ190cmVlX2xsLHRyZWVfY2x1c3RlcnNfaW5kLGZlYXR1cmVfbWVhbl9icmFuY2hfbGVuZ3RoLGZlYXR1cmVfbWVhbl9pbnRlcm5hbF9icmFuY2hfbGVuZ3RoLGZlYXR1cmVfbWVhbl9sZWFmX2JyYW5jaF9sZW5ndGgsZmVhdHVyZV90cmVlX01BRCxmZWF0dXJlX21lYW5fcmZfZGlzdGFuY2UpCmBgYApgYGB7cn0KTUxfZWRpdGVkX2ZlYXR1cmVzICU+JSBmaWx0ZXIgKG1zYV9wYXRoPT0iL2dyb3Vwcy9wdXBrby9ub2Fla2VyL2RhdGEvQUJDX0RSL1BBTkRJVC9QRjAwMDA1L3JlZl9tc2EuYWEucGh5IikKYGBgCgoKYGBge3J9CnRyZWVfZmVhdHVyZXNfYW5hbHlzaXMgPSByZWFkX3RzdigiL1VzZXJzL25vYS9Xb3Jrc3BhY2UvcmF4bWxfZGVlcF9sZWFybmluZ19yZXN1bHRzL3JlYWR5X3Jhd19kYXRhL0FsbF9kYXRhL3RyZWVfY29tcGFyaXNvbnMudHN2IikKYGBgCmBgYHtyfQp0cmVlX2ZlYXR1cmVzX2FuYWx5c2lzICU+JSBoZWFkKCkKYGBgCgoKCmBgYHtyfQp0cmVlX2ZlYXR1cmVzX2FuYWx5c2lzICU+JSBoZWFkKDUpCnRyZWVfZmVhdHVyZXNfYW5hbHlzaXNfZWRpdGVkPC0gdHJlZV9mZWF0dXJlc19hbmFseXNpcyAlPiUgbXV0YXRlIChMTF9kaWZmID0gZGVsdGFfbGxfZnJvbV9vdmVyYWxsX21zYV9iZXN0X3RvcG9sb2d5X290aGVyLWRlbHRhX2xsX2Zyb21fb3ZlcmFsbF9tc2FfYmVzdF90b3BvbG9neSwgc3RhcnRpbmdfdHJlZV9sbF9kaWZmID0gc3RhcnRpbmdfdHJlZV9sbC0gc3RhcnRpbmdfdHJlZV9sbF9vdGhlcikgJT4lIG11dGF0ZSAoaXNfYmV0dGVyID0gTExfZGlmZj4wLjEpCgp0cmVlX2ZlYXR1cmVzX2FuYWx5c2lzX2VkaXRlZCAlPiUgZ2dwbG90KGFlcyh4ID0gcmZfZGlzdF9zdGFydGluZ190cmVlcywgeT0gcmZfZGlzdF9maW5hbF90cmVlcykpICsgZ2VvbV9wb2ludCgpKyBmYWNldF9ncmlkKHJvd3MgPSB2YXJzKHN0YXJ0aW5nX3RyZWVfdHlwZSksIGNvbHMgPSB2YXJzKHN0YXJ0aW5nX3RyZWVfdHlwZV9vdGhlcikpCgoKYGBgCmBgYHtyfQp0cmVlX2ZlYXR1cmVzX2FuYWx5c2lzX2VkaXRlZCAlPiUgZmlsdGVyIChzdGFydGluZ190cmVlX3R5cGU9PXN0YXJ0aW5nX3RyZWVfdHlwZV9vdGhlciwpICU+JSAgZ2dwbG90KGFlcyh5ID0gKExMX2RpZmYpLCB4PShzdGFydGluZ190cmVlX2xsX2RpZmYpKSkgKyBnZW9tX3BvaW50KCkrIGZhY2V0X2dyaWQocm93cyA9IHZhcnMoc3RhcnRpbmdfdHJlZV90eXBlKSkKCnRyZWVfZmVhdHVyZXNfYW5hbHlzaXNfZWRpdGVkICU+JSBmaWx0ZXIgKHN0YXJ0aW5nX3RyZWVfdHlwZT09c3RhcnRpbmdfdHJlZV90eXBlX290aGVyLCkgJT4lICBnZ3Bsb3QoYWVzKHkgPSAocmZfZGlzdF9maW5hbF90cmVlcyksIHg9YWJzKChzdGFydGluZ190cmVlX2xsX2RpZmYpKSkpICsgZ2VvbV9wb2ludCgpKyBmYWNldF9ncmlkKHJvd3MgPSB2YXJzKHN0YXJ0aW5nX3RyZWVfdHlwZSkpCmBgYAoKYGBge3J9CgpkYXRhX2Zvcl9NTDwtdHJlZV9mZWF0dXJlc19hbmFseXNpc19lZGl0ZWQgJT4lIHNlbGVjdCAoLXN0YXJ0aW5nX3RyZWVfaW5kLC1tc2FfcGF0aF9vdGhlciAsIC1zdGFydGluZ190cmVlX2luZF9vdGhlciwgLXN0YXJ0aW5nX3RyZWVfb2JqZWN0LCAtc3RhcnRpbmdfdHJlZV9vYmplY3Rfb3RoZXIsLWRlbHRhX2xsX2Zyb21fb3ZlcmFsbF9tc2FfYmVzdF90b3BvbG9neV9vdGhlciwgLWZpbmFsX2xsX290aGVyLCAtZmluYWxfdHJlZV90b3BvbG9neSwgLWZpbmFsX3RyZWVfdG9wb2xvZ3lfb3RoZXIsLXN0YXJ0aW5nX3RyZWVfdHlwZSwtc3RhcnRpbmdfdHJlZV90eXBlX290aGVyICwtZmVhdHVyZV9tc2FfdHlwZSwtTExfZGlmZiwtcmZfZGlzdF9maW5hbF90cmVlcywtZGVsdGFfbGxfZnJvbV9vdmVyYWxsX21zYV9iZXN0X3RvcG8gICkKCm1zYXMgPSB0cmVlX2ZlYXR1cmVzX2FuYWx5c2lzX2VkaXRlZCAlPiUgZGlzdGluY3QgKG1zYV9wYXRoKSAlPiUgcHVsbChtc2FfcGF0aCkKdGVzdF9zYW1wbGVkX21zYXMgPSBtc2FzW3NhbXBsZSgxOmxlbmd0aChtc2FzKSwyMCldCnRlc3Q8LSAgZGF0YV9mb3JfTUwgJT4lIGZpbHRlciAobXNhX3BhdGggJWluJSB0ZXN0X3NhbXBsZWRfbXNhcykgJT4lIHNlbGVjdCAoLW1zYV9wYXRoKQp0cmFpbjwtICBkYXRhX2Zvcl9NTCAlPiUgZmlsdGVyICghKG1zYV9wYXRoICVpbiUgdGVzdF9zYW1wbGVkX21zYXMpKSAlPiUgc2VsZWN0ICgtbXNhX3BhdGgpCgoKYmluX2dsbTwtICBnbG0oaXNfYmV0dGVyIH4gLiAsIGRhdGEgPSB0cmFpbiwgZmFtaWx5ID0gImJpbm9taWFsIikKY2FyZXQ6OnZhckltcChiaW5fZ2xtKQoKbnVsbG1vZCA8LSBnbG0oaXNfYmV0dGVyIH4xLGRhdGEgPSB0cmFpbiAsIGZhbWlseT0iYmlub21pYWwiKQpyMiA9IDEtbG9nTGlrKGJpbl9nbG0pL2xvZ0xpayhudWxsbW9kKQpwcmludChyMikKc3VtbWFyeShiaW5fZ2xtKQoKYGBgCgpBZGQgYSBuZXcgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpJbnNlcnQgQ2h1bmsqIGJ1dHRvbiBvbiB0aGUgdG9vbGJhciBvciBieSBwcmVzc2luZyAqQ21kK09wdGlvbitJKi4KCldoZW4geW91IHNhdmUgdGhlIG5vdGVib29rLCBhbiBIVE1MIGZpbGUgY29udGFpbmluZyB0aGUgY29kZSBhbmQgb3V0cHV0IHdpbGwgYmUgc2F2ZWQgYWxvbmdzaWRlIGl0IChjbGljayB0aGUgKlByZXZpZXcqIGJ1dHRvbiBvciBwcmVzcyAqQ21kK1NoaWZ0K0sqIHRvIHByZXZpZXcgdGhlIEhUTUwgZmlsZSkuIAoKVGhlIHByZXZpZXcgc2hvd3MgeW91IGEgcmVuZGVyZWQgSFRNTCBjb3B5IG9mIHRoZSBjb250ZW50cyBvZiB0aGUgZWRpdG9yLiBDb25zZXF1ZW50bHksIHVubGlrZSAqS25pdCosICpQcmV2aWV3KiBkb2VzIG5vdCBydW4gYW55IFIgY29kZSBjaHVua3MuIEluc3RlYWQsIHRoZSBvdXRwdXQgb2YgdGhlIGNodW5rIHdoZW4gaXQgd2FzIGxhc3QgcnVuIGluIHRoZSBlZGl0b3IgaXMgZGlzcGxheWVkLgpgYGB7cn0Kc3VtbWFyeSh0cmFpbikKYGBgCgo=